package com.cy.pj.common.Aspect;

import java.lang.reflect.Method;
import java.util.Arrays;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.cy.pj.common.util.IPUtils;
import com.cy.pj.sys.pojo.SysLog;
import com.cy.pj.sys.service.SysLogService;

import lombok.extern.slf4j.Slf4j;
/***
 * @Aspect 注解描述的类型为spring AOP中的切面对象类型,对此对象中可以封装:
 * 1)切入点(定义在哪些目标对象的那些方法上进行功能扩展)
 * 2)通知(封装功能扩展的业务逻辑)
 * @author 15565
 *
 */
@Slf4j
@Order(1)
@Aspect
@Component
public class SysLogAcpect {
	/***
	 * @Pointcut 注解用于定义切入点,具体方式可以基于特定表达式进行实现,例如:
	 * 1)bean作为一种切入点表达式类型
	 * 2)sysUserServiceImpl 为 spring容器中的一个bean的名字
	 * 	 这里的含义是当这个bean对象的任意方法执行时都由本切面对象的通知方法做功能增强
	 */
	@Pointcut("bean(sysUserServiceImpl)")
	public void doLogPointCut() {}

	/** 环绕通知:此环绕通知使用的切入点为sysUserServiceImpl
	 *	环绕通知特点:
	 *1)编写:
	 *方法的返回值为Object,
	 *方法参数为ProceedingJoinPoint类型 
	 *方法跑出异常为 throws Throwable
	 *2)应用
	 *目标方法执行之前或之后都可以进行功能扩展
	 *相对于其他同志优先级最高
	 * @param jp 为一个连接对象(封装了正在要执行的目标方法信息)
	 * @return 方法的执行结果
	 */
	@Around(value="doLogPointCut()")
	public Object around(ProceedingJoinPoint jp) throws Throwable {
		System.out.println("SysLogAspect.@Around.before");
		long start=System.currentTimeMillis();
		log.info("method start {}",start);
		try {
			Object result=jp.proceed();//最终会执行目标方法
			long end=System.currentTimeMillis();
			log.info("method end {}",end);
			System.out.println("SysLogAspect.Around.after");
			//将用户的正常的行为信息写入到数据库
			 saveUserLog(jp,(end-start));
			return result;
		}catch(Throwable e) {
			log.error("method error {},error msg is {}",
					System.currentTimeMillis(),e.getMessage());
			throw e;
		}

	}
	@Autowired
	private SysLogService sysLogService;
	private void saveUserLog(ProceedingJoinPoint jp,long time)throws Exception{
		//1.获取用户行为日志信息
		   //获取目标对象(要执行的那个目标业务对象)类型
		   Class<?> targetCls=jp.getTarget().getClass();
		   //获取方法签名对象(此对象中封装了要执行的目标方法信息)
		   MethodSignature ms=(MethodSignature) jp.getSignature();
		   //获取目标方法对象，基于此对象获取方法上的RequiredLog注解，进而取到操作名
		   Method targetMethod=targetCls.getMethod(ms.getName(),ms.getParameterTypes());
		   RequiredLog required=targetMethod.getAnnotation(RequiredLog.class);
		   //获取操作名
		   String operation="operation";
		   if(required!=null) {//注解方式的切入点无须做此判断
			   operation=required.value();
		   }
		   //获取目标方法类全名
		   String targetMethodName=targetCls.getName()+"."+ms.getName();
		   //2.构建用户行为日志对象封装用户行为信息
		   SysLog log=new SysLog();
		   log.setIp(IPUtils.getIpAddr());
		   log.setUsername("liuqing");//将来的登陆用户，现在可以先写个假数据
		   log.setOperation(operation);//不知道该写什么
		   log.setMethod(targetMethodName);//类全名.方法名
		   log.setParams(Arrays.toString(jp.getArgs()));
		   log.setTime(time);
		   log.setCreatedTime(new java.util.Date());
		   //3.用户行为日志写入到数据库
		   sysLogService.saveObject(log);
	}
}






























